// Vue3的响应式 -> Proxy 


// 存储副作用函数的桶
const bucket = new WeakMap();
// 用一个全局变量存储被注册的副作用函数
let activeEffect

// effect栈 解决effectFn嵌套的问题。
let effectStack = []

// effect 函数用于注册副作用函数
function effect(fn, options = {}) {
  const effectFn = () => {
    // 将副作用函数从依赖集合中移除
    clearup(effectFn)
    // 当前effectFn执行时，将其设置为当前激活的副作用函数
    activeEffect = effectFn
    // 在调用副作用函数之前，将当前副作用函数压入栈中
    effectStack.push(effectFn)
    fn()
    // 在当前副作用函数执行完毕之后， 将当前的副作用函数弹出栈，并把activeEffect还原为之前的值
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  effectFn.options = options
  // effectFn.deps用来存储所有与该副作用函数相关联的依赖集合
  effectFn.deps = []
  // 执行副作用函数
  effectFn()
}

// 在 get 拦截函数内调用 track 函数追踪变化
function track(target, key) {
  // 没有activeEffect，直接return
  if (!activeEffect) return
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, (deps = new Set()))
  }
  // 把当前激活的副作用函数添加到依赖集合deps中
  deps.add(activeEffect)
  // deps就是一个与当前副作用函数存在联系的依赖集合
  // 将其添加到activeEffect.deps 数组中
  activeEffect.deps.push(deps)  //新增
}

// 在 set 拦截函数内调用 trigger 函数触发变化
function trigger(target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)
  // 构造另外一个set集合并遍历它。避免无限执行
  const effectToRun = new Set()
  effects && effects.forEach(effectFn => {
    //如果 trigger触发执行的副作用函数 与 当前正在执行的副作用函数相同，则不触发执行,避免无限递归
    if (effectFn !== activeEffect) {
      effectToRun.add(effectFn)
    }
  })
  effectToRun.forEach(effectFn => {
    // 如果一个副作用函数存在调度器，则调用该调度器，并将副作用函数作为参数传递
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn)
    } else
      effectFn()
  })
}

// 在依赖集合中删除当前的副作用函数
function clearup(effectFn) {
  //遍历 effectFn.deps 数组
  for (let i = 0; i < effectFn.deps.length; i++) {
    // 其中deps是依赖集合
    const deps = effectFn.deps[i]
    // 将effectFn从依赖集合中移除
    deps.delete(effectFn)
  }
  // 最后需要重置 effectFn.deps数组
  effectFn.deps.length = 0
}



// 原始数据
const data = { text: "hello world rays77" }

const obj = new Proxy(data, {
  // 拦截读取操作
  get(target, key) {
    // 将副作用函数 activeEffect 添加到存储副作用函数的桶中
    track(target, key)
    // 返回属性值
    return target[key]
  },
  // 拦截设置操作
  set(target, key, newValue) {
    // 设置属性
    target[key] = newValue
    // 把副作用函数从桶中取出并执行
    trigger(target, key)
  }
})



data.text = '和不可以'

console.log('data.text -> ', data.text)